/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author jefoutta
 */
import java.util.*;
import java.io.*;

public class Interpreter {

    private Hashtable<String, Integer> inst_table = new Hashtable();//Poseen como segundo tipo Object porque aún no se cual va a ser ese tipo
    private SystemCalls system_calls = new SystemCalls();
    private StringTokenizer tokenizer;
    private StringTokenizer labelToken;
    private StringTokenizer parametros;
    private FileManager fm = new FileManager();
    public  FileManager log = new FileManager();
    private boolean syn;
    private ArrayList<FileManager> fileList = new ArrayList();
    private Integer procAct;
    private boolean subIO = false;
    private boolean semaphore = false;
    private boolean tickCheck = false;
    private boolean end = false;
    private Integer temp1 = 0;

//Se inicializa el hashtable que contiene el nombre de las instrucciones y a este se le da un indice para poder identificarlo en un switch
    public Interpreter(){
        inst_table.put("ADD", 1);
        inst_table.put("SET", 2);
        inst_table.put("SUB", 3);
        inst_table.put("MUL", 4);
        inst_table.put("DIV", 5);
        inst_table.put("ECHO", 6);
        inst_table.put("OPENFILE", 7);
        inst_table.put("CLOSEFILE", 8);
        inst_table.put("READLINE", 9);
        inst_table.put("JMP", 10);
        inst_table.put("FORK", 11);
        inst_table.put("SLEEP", 12);
        inst_table.put("GETSTATE",13);
        inst_table.put("END", 14);
        inst_table.put("RANDOM", 15);
        inst_table.put("WAIT", 16);
        inst_table.put("SIGNAL", 17);
    }

//Este método elimina lineas en blanco, comentarios y en una hastable inserta las etiquetas con su número de linea respectivo
    
    public ArrayList<String> tLink(ArrayList<String> tempArray){
        int i = 0;
        while(i < tempArray.size()){
            if(tempArray.get(i).length() == 0){
                tempArray.remove(i);
                i--;
            }else
                if(tempArray.get(i).contains(";")){
                    tempArray.remove(i);
                    i--;
                }
            i++;
        }
        i = 0;
        String s;
        while(i < tempArray.size()){
            if(tempArray.get(i).contains("'")){
                labelToken = new StringTokenizer(tempArray.get(i), "'");
                s = labelToken.nextToken().toUpperCase();
                this.foundLabel(s, (i-1));
                tempArray.remove(i);
                i--;
            }
            i++;
        }
        Log.println(PCB.proc_id.get(Scheduling.procAct).pname + ": " +  tempArray.toString());
        return tempArray;
    }

//Este método se encarga de crear el archivo de salida si es asincronico
    public void interpreter(Integer procId) throws IOException{
        procAct = procId;
        syn = PCB.proc_id.get(procId).syn;
        if(syn){
            log.createFile(PCB.proc_id.get(procId).fname);
            fileList.add(log);
        }
        parametros = new StringTokenizer(PCB.proc_id.get(procId).pname," ");
        fm.open(parametros.nextToken());
        int i = 0;
        String ptemp;
        while(parametros.hasMoreTokens()){
            ptemp = parametros.nextToken();
            if(!ptemp.contains("p")){
                Registries.reg_table.get("R"+i).setValue(Integer.parseInt(ptemp));
                i++;
            }
        }
        ArrayList<String> temp = this.tLink(fm.readAll());
        Memory.setMemory(temp);
        toExec();
    }

//Este metodo manda a interpretar linea por linea al proceso
    public void toExec()throws IOException{
        while((Memory.ip < Memory.memory_segment.size()) && !(subIO) && !(tickCheck) && !(end)){
            exec(Memory.memory_segment.get(Memory.ip));
            Scheduling.setJob(Scheduling.procAct);
            tickCheck = true;
            Memory.ip++;
        }
        if(tickCheck){
            Scheduling.tick = true;
            tickCheck = false;
        }
        if (subIO){
                temp1++;
                Scheduling.IO = true;

                subIO = false;
                if(Scheduling.procAct != null){
                    PCB.proc_id.get(Scheduling.procAct).IO = true;
                    PCB.proc_id.get(Scheduling.procAct).setState(4);
                }    
            }
        
        if(end){
            Scheduling.end = true;
            end = false;
        }

        if (semaphore){
            Scheduling.semaphore = true;
            semaphore = false;
        }
        if(syn)
            log.closew();
    }

//Este proceso interpreta las instrucciones de cada linea y las ejecuta.
    public void exec(String s) throws IOException{
        String firstToken;
        String toPrint = null;
        tokenizer = new StringTokenizer(s, " ");
        firstToken = tokenizer.nextToken().toUpperCase();
        switch (inst_table.get(firstToken)){
            case 1:this.parseAdd(tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase());
            break;
            case 2:this.parseSet(tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase());
            break;
            case 3:this.parseSub(tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase());
            break;
            case 4:this.parseMul(tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase());
            break;
            case 5:this.parseDiv(tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase());
            break;
            case 6:{
                subIO = true;
                String temp = "";
                String temp2 = "";
                while(tokenizer.hasMoreTokens()){
                    temp2 = tokenizer.nextToken();
                    if(temp2.startsWith("\"") && (temp2.endsWith("\"")) && (temp2.length() > 1))
                        temp = temp + temp2.substring(1,temp2.length() - 1);
                    else
                        if (temp2.startsWith("\"")){
                        temp = temp + temp2.substring(1) +" ";
                    }else
                        if(temp2.endsWith("\""))
                            temp = temp + temp2.substring(0, temp2.length() -1);
                        else
                            temp = temp + temp2;
                }
                toPrint = system_calls.parseEcho(temp);
            }
            break;
            case 7:{
                subIO = true;
                String temp = "";
                String temp2 = "";
                while(!(temp2.endsWith("\""))){
                    temp2 = tokenizer.nextToken();
                    if(temp2.startsWith("\"") && (temp2.endsWith("\"")) && (temp2.length() > 1))
                        temp = temp + temp2.substring(1,temp2.length() - 1);
                    else
                        if (temp2.startsWith("\"")){
                        temp = temp + temp2.substring(1) +" ";
                    }else
                        if(temp2.endsWith("\""))
                            temp = temp + temp2.substring(0, temp2.length() -1);
                        else
                            temp = temp + temp2 + " ";
                }
                system_calls.parseOpenFile(temp, tokenizer.nextToken().toUpperCase());
            }
            break;
            case 8:{
                subIO = true;
                system_calls.parseCloseFile(tokenizer.nextToken().toUpperCase());
            }
            break;
            case 9:{
                subIO = true;
                toPrint = system_calls.parseReadLine(tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase());
            }
            break;
            case 10: this.parseJump(tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase());
            break;
            case 11:{
                String temp = "";
                String temp2 = "";
                while(!(temp2.endsWith("\""))){
                    temp2 = tokenizer.nextToken();
                    if(temp2.startsWith("\"") && (temp2.endsWith("\"")) && (temp2.length() > 1))
                        temp = temp + temp2.substring(1,temp2.length() - 1);
                    else
                        if (temp2.startsWith("\"")){
                        temp = temp + temp2.substring(1) +" ";
                    }else
                        if(temp2.endsWith("\""))
                            temp = temp + temp2.substring(0, temp2.length() -1);
                        else
                            temp = temp + temp2 + " ";
                }
                system_calls.parseFork(temp, tokenizer.nextToken().toUpperCase());
            }
            break;
            case 12:{
                subIO = true;
            }
            break;
            case 13: system_calls.parseGetState(tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase());
            break;
            case 14:{
                system_calls.parseEnd();
                end = true;
                //System.out.println(PCB.proc_id.get(ProcQueue.procQueues.get(1).peek()).totalWaitingTime);
                //System.out.println(PCB.proc_id.get(Scheduling.procAct).totalWaitingTime);
                //Scheduling.procAct = null;
            }
            break;
            case 15: this.parseRandom(tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase(), tokenizer.nextToken().toUpperCase());
            break;
            case 16:{
                if(system_calls.parseWait(tokenizer.nextToken().toUpperCase())){
                    semaphore = true;
                }
            }
            break;
            case 17:{
                system_calls.parseSignal(tokenizer.nextToken().toUpperCase());
            }
            break;
            default:{
            }
        }
        if(toPrint != null){
            if(syn){
                log.writeLine(toPrint);
            }else{
                //Terminal.print_queue.addLast(toPrint);
                if(toPrint.endsWith("\\n")){
                    toPrint = toPrint.substring(0,toPrint.indexOf("\\"));
                    System.out.println(toPrint);
                }else
                    System.out.print(toPrint);
            }
        }
    }

//Estas son todas las funciones que ejectua el interprete (no incluyen las system calls)
    public void parseSet(String s1, String s2){
        if(Registries.reg_table.containsKey(s1))
            if(Registries.reg_table.containsKey(s2))
                this.set(Registries.reg_table.get(s1), Registries.reg_table.get(s2));
            else
                this.set(Registries.reg_table.get(s1), Integer.parseInt(s2));
    }

    public void parseAdd(String s1, String s2, String s3){
        if((Registries.reg_table.containsKey(s3)) && (Registries.reg_table.containsKey(s1)) && (Registries.reg_table.containsKey(s2)))
            Registries.reg_table.get(s1).setValue(this.add(Registries.reg_table.get(s2),Registries.reg_table.get(s3)));
    }

    public void parseSub(String s1, String s2, String s3){
        if((Registries.reg_table.containsKey(s1)) && (Registries.reg_table.containsKey(s2)) && (Registries.reg_table.containsKey(s3)))
            Registries.reg_table.get(s1).setValue(this.sub(Registries.reg_table.get(s2),Registries.reg_table.get(s3)));
    }

    public void parseMul(String s1, String s2, String s3){
        if((Registries.reg_table.containsKey(s1)) && (Registries.reg_table.containsKey(s2)) && (Registries.reg_table.containsKey(s3)))
            Registries.reg_table.get(s1).setValue(this.mul(Registries.reg_table.get(s2), Registries.reg_table.get(s3)));
    }

    public void parseDiv(String s1, String s2, String s3, String s4){
        if((Registries.reg_table.containsKey(s1)) && (Registries.reg_table.containsKey(s2)) && (Registries.reg_table.containsKey(s3)) && (Registries.reg_table.containsKey(s4))){
            Registries.reg_table.get(s1).setValue(this.div(Registries.reg_table.get(s2), Registries.reg_table.get(s3)));
            Registries.reg_table.get(s4).setValue(this.mod(Registries.reg_table.get(s2), Registries.reg_table.get(s3)));
        }
    }

    public void parseRandom(String s1, String s2, String s3){
        if((Registries.reg_table.containsKey(s1)) && (Registries.reg_table.containsKey(s2)) && (Registries.reg_table.containsKey(s3))){
            Registries.reg_table.get(s3).setValue(this.random(Registries.reg_table.get(s1), Registries.reg_table.get(s2)));
        }
    }

    public void parseJump(String s1, String s2){
        if(Registries.reg_table.containsKey(s1))
            this.jump(Registries.reg_table.get(s1), s2);
    }

    public void foundLabel(String s1, Integer line){
        Memory.label_table.put(s1,line);
    }

    public void set(Registry rt1, Integer val){
        Registries.reg_table.get(rt1.getName()).setValue(val);
    }

    public void set(Registry rt1, Registry rt2){ //anteriormente en lugar de strings eran Registry
        Registries.reg_table.get(rt1.getName()).setValue(Registries.reg_table.get(rt2.getName()).getValue());
    }

    public Integer add(Registry rt1, Registry rt2){
        return Registries.reg_table.get(rt1.getName()).getValue() + Registries.reg_table.get(rt2.getName()).getValue();
    }

    public Integer sub(Registry rt1, Registry rt2){
        return Registries.reg_table.get(rt1.getName()).getValue() - Registries.reg_table.get(rt2.getName()).getValue();
    }

    public Integer mul(Registry rt1, Registry rt2){
        return Registries.reg_table.get(rt1.getName()).getValue() * Registries.reg_table.get(rt2.getName()).getValue();
    }

    public Integer div(Registry rt1, Registry rt2){
        return Registries.reg_table.get(rt1.getName()).getValue() / Registries.reg_table.get(rt2.getName()).getValue();
    }

    public Integer mod(Registry rt1, Registry rt2){
        return Registries.reg_table.get(rt1.getName()).getValue() % Registries.reg_table.get(rt2.getName()).getValue();
    }

    public Integer random(Registry rt1, Registry rt2){
        
        Double d = Registries.reg_table.get(rt1.getName()).getValue() + Math.random()*(Registries.reg_table.get(rt2.getName()).getValue() - Registries.reg_table.get(rt1.getName()).getValue());
        return d.intValue();
    }

    public void jump(Registry rt1, String label){
        if(Registries.reg_table.get(rt1.getName()).getValue() > 0)
            Memory.ip = Memory.label_table.get(label);
    }
    
    public Integer getState(Integer val, Registry rt1){
        return PCB.proc_id.get(val).getState();
    }
}
